/*
author:杨文超
email:yangwenchao@keynection.cn
*/
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_sleep.h"
#include "esp_netif_sntp.h"
#include "lwip/ip_addr.h"
#include "esp_sntp.h"
#include "esp_err.h"
#include "geminai_log.h"
#include "geminai_sntp.h"

static const char *TAG = "GEMINAI_SNTP";

#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 48
#endif

RTC_DATA_ATTR static time_t last_sntp_time=0;
// RTC_DATA_ATTR static int boot_count = 0;

uint32_t wakeup_cause;

esp_err_t sntp_config_init(int mode);
void print_sntp_servers(void);

void time_sync_notification_cb(struct timeval *tv)
{
    GEMINAI_LOGI(TAG, "Notification of a time synchronization event");
    time_t now;
    struct tm timeinfo;
    char strftime_buf[64];
    unsigned long diff;


    last_sntp_time=tv->tv_sec;

    time(&now);
    diff = (time_t)llabs(tv->tv_sec-now);
    localtime_r(&now, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    GEMINAI_LOGI(TAG, "The current date/time is: %s", strftime_buf);
    localtime_r(&tv->tv_sec, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    GEMINAI_LOGI(TAG, "The sntp notification current date/time is: %s", strftime_buf);
    if (diff >= 60)
    {
        struct timezone tz;
        tz.tz_minuteswest = 480; // 东8区 * 60 分钟/小时
        tz.tz_dsttime = 0; // 不使用夏令时
        settimeofday(tv, &tz);
    }
    else
    {
        if (sntp_get_sync_mode() == SNTP_SYNC_MODE_SMOOTH) {
            struct timeval outdelta;
            while (sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) {
                adjtime(NULL, &outdelta);
                GEMINAI_LOGI(TAG, "Waiting for adjusting time ... outdelta = %jd sec: %li ms: %li us",
                        (intmax_t)outdelta.tv_sec,
                        outdelta.tv_usec/1000,
                        outdelta.tv_usec%1000);
                vTaskDelay(2000 / portTICK_PERIOD_MS);
            }
        }
    } 
    time(&now);
    localtime_r(&now, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    GEMINAI_LOGI(TAG, "After sntp syn the current date/time is: %s", strftime_buf);
}


void print_sntp_servers(void)
{
    GEMINAI_LOGI(TAG, "List of configured NTP servers:");

    for (uint8_t i = 0; i < SNTP_MAX_SERVERS; ++i){
        if (esp_sntp_getservername(i)){
            GEMINAI_LOGI(TAG, "server %d: %s", i, esp_sntp_getservername(i));
        } else {
            // we have either IPv4 or IPv6 address, let's print it
            char buff[INET6_ADDRSTRLEN];
            ip_addr_t const *ip = esp_sntp_getserver(i);
            if (ipaddr_ntoa_r(ip, buff, INET6_ADDRSTRLEN) != NULL)
                GEMINAI_LOGI(TAG, "server %d: %s", i, buff);
        }
    }
}


esp_err_t sntp_config_init(int mode)
{
    /* 中国科学院国家授时中心NTP服务 ntp.ntsc.ac.cn
    阿里云NTP服务器 ntp.aliyun.com
     */
    esp_err_t ret;
    esp_sntp_config_t sntp_config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(3,ESP_SNTP_SERVER_LIST("ntp.aliyun.com", "ntp.ntsc.ac.cn","pool.ntp.org" ) );

    sntp_config.sync_cb = time_sync_notification_cb;     // Note: This is only needed if we want
    sntp_config.start = false;
    if (SNTP_SYNC_MODE_SMOOTH == mode ) {
        sntp_config.smooth_sync = true;
    } else if( SNTP_SYNC_MODE_IMMED == mode ) {
        sntp_config.smooth_sync = false;
    } else {
        return ESP_ERR_INVALID_ARG;
    }
    ret = esp_netif_sntp_init(&sntp_config);
    return ret;
}


void sntp_task(void *pvParameters)
{
    time_t now = 0;
    struct tm timeinfo = { 0 };

    wakeup_cause = esp_sleep_get_wakeup_cause();
    GEMINAI_LOGI(TAG, "cause %lu", wakeup_cause);
    // time_t sntp_time_difference = 0;

    time(&now);
    localtime_r(&now, &timeinfo);
    GEMINAI_LOGI(TAG, "Starting SNTP");
    // if (last_sntp_time != 0) {
    //     sntp_time_difference = llabs(last_sntp_time-now);
    // }

    sntp_config_init(SNTP_SYNC_MODE_SMOOTH);
    print_sntp_servers();
    esp_netif_sntp_start();
    int retry = 0;
    const int retry_count = 15;
    while (esp_netif_sntp_sync_wait(3000 / portTICK_PERIOD_MS) == ESP_ERR_TIMEOUT && ++retry < retry_count) {
        GEMINAI_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
    }
    if(retry == 15)
    {
        GEMINAI_LOGI(TAG, "Can not connect to sntp server");
        return;
    }

    if (sntp_get_sync_mode() == SNTP_SYNC_MODE_SMOOTH) {
        struct timeval outdelta;
        while (sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) {
            adjtime(NULL, &outdelta);
            GEMINAI_LOGI(TAG, "Waiting for adjusting time ... outdelta = %jd sec: %li ms: %li us",
                        (intmax_t)outdelta.tv_sec,
                        outdelta.tv_usec/1000,
                        outdelta.tv_usec%1000);
            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }
    }
    vTaskDelete(NULL);
}


void sntp_task_start()
{
    static bool start= false;
    if(start == false)
        xTaskCreate(sntp_task, "sntp_task", 4096, NULL, 12, NULL);
    start = true;
}
